package com.young.es.rest.template;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.young.es.rest.exception.EsExecption;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.*;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.Map;

public class EsRestTemplate {
    private RestHighLevelClient restHighLevelClient;

    public EsRestTemplate(RestHighLevelClient restHighLevelClient) {
        this.restHighLevelClient = restHighLevelClient;
    }

    Logger logger = LoggerFactory.getLogger("EsRestClientService");
    /**
     * 判断索引是否存在
     *
     * @param index
     * @return
     * @throws IOException
     */
    public boolean isExistIndex(String index) throws IOException {
        GetIndexRequest request = new GetIndexRequest(index);
        boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        return exists;
    }

    /**
     * 根据字符串mapping 创建索引
     *
     * @param index
     * @param mapping
     * @throws IOException
     */
    public Boolean createIndex(String index, String mapping) throws IOException {
        CreateIndexRequest request = new CreateIndexRequest(index);
        request.mapping(mapping, XContentType.JSON);
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        return createIndexResponse.isAcknowledged();
    }


    /**
     * 根据Map 创建索引
     *
     * @param index
     * @param mapping
     * @throws IOException
     */
    public boolean createIndex(String index, Map mapping) throws IOException {
        CreateIndexRequest request = new CreateIndexRequest(index);
        request.mapping(mapping);
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        return createIndexResponse.isAcknowledged();
    }


    public boolean existAlia(String index, String alias) {
        GetAliasesRequest request = new GetAliasesRequest();
        request.indices(index);
        request.aliases(alias);
        try {
            boolean exists = restHighLevelClient.indices().existsAlias(request, RequestOptions.DEFAULT);
            return exists;
        } catch (IOException e) {
            e.printStackTrace();
            throw new EsExecption("Es 判断是否存在别名出现异常");
        }
    }

    /**
     * 删除索引
     *
     * @param index
     * @return
     * @throws IOException
     */
    public boolean deleteIndex(String index) throws IOException {
        try {
            DeleteIndexRequest request = new DeleteIndexRequest(index);
            AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
            if (!response.isAcknowledged()) {
                return response.isAcknowledged();
            }
        } catch (ElasticsearchException exception) {
            if (exception.status() == RestStatus.NOT_FOUND) {
                logger.error("{}索引不存在", index);
            }
        }
        return true;
    }


    /**
     * 为索引添加别名
     *
     * @param index
     * @param alias
     */
    public void addAlias(String index, String alias) {
        IndicesAliasesRequest request = new IndicesAliasesRequest();
        IndicesAliasesRequest.AliasActions aliasAction =
                new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
                        .index(index)
                        .alias(alias);
        request.addAliasAction(aliasAction);
        try {
            AcknowledgedResponse response = restHighLevelClient.indices().updateAliases(request, RequestOptions.DEFAULT);
            boolean acknowledged = response.isAcknowledged();
            if (acknowledged) {
                logger.info("index:{}, alias:{},别名添加成功", index, alias);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 保存文档
     *
     * @param index
     * @param document
     */
    public void saveOrUpdateDocument(String index, Map document, String documentId) throws IOException {
        IndexRequest indexRequest = new IndexRequest(index).source(document);
        if (!StringUtils.isEmpty(documentId)) {
            indexRequest.id(documentId);
        }
        try {
            IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
            String id = response.getId();
            if (response.getResult() == DocWriteResponse.Result.CREATED) {
                logger.info("文档创建成功，index:{},documentId:{}", index, id);
            } else if (response.getResult() == DocWriteResponse.Result.UPDATED) {
                logger.info("文档更新成功，index:{},documentId:{}", index, id);
            }
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.CONFLICT) {
                logger.info("版本冲突，index:{},document:{}", index, document);
            }
        }
    }

    /**
     * 判断文档是否存在
     *
     * @param index
     * @param documentId
     * @return
     */
    public Boolean existDocument(String index, String documentId) {
        GetRequest getRequest = new GetRequest(index, documentId);
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        try {
            boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
            return exists;
        } catch (IOException e) {
            e.printStackTrace();
            throw new EsExecption("Es请求异常");
        }
    }


    /**
     * 存储文档到索引中
     *
     * @param index
     * @param document
     */
    public String saveOrUpdateDocument(String index, Object document, String documentId) {
        try {
            if (document == null) {
                throw new EsExecption("文档不能为null");
            }
            ObjectMapper mapper = new ObjectMapper();
            String documentJson = mapper.writeValueAsString(document);
            IndexRequest request = new IndexRequest(index);
            request.source(documentJson, XContentType.JSON);
            request.id(documentId);

            IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
            String id = indexResponse.getId();
            return id;
        } catch (IOException e) {
            e.printStackTrace();
            throw new EsExecption("索引失败");
        }
    }

    /**
     * 删除文档
     */
    public void deleteDocument(String index, String documentId) {
        DeleteRequest request = new DeleteRequest(index, documentId);
        try {
            DeleteResponse deleteResponse = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
            if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
                throw new EsExecption("索引不存在");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 搜索
     *
     * @param index
     * @param sourceBuilder
     * @return
     */
    public SearchResponse search(String index, SearchSourceBuilder sourceBuilder, Boolean isScroll) {
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.source(sourceBuilder);
        if (isScroll) {
            // 搜索超时时间30秒
            searchRequest.scroll(TimeValue.timeValueMinutes(30));
        }
        logger.info("es 查询语句：{}", sourceBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            return searchResponse;
        } catch (IOException e) {
            e.printStackTrace();
            throw new EsExecption("搜索异常");
        }
    }


    /**
     * 根据scrollId 滚动搜索`
     *
     * @param scrollId
     * @return
     */
    public SearchResponse searchScroll(String scrollId) {
        SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
        scrollRequest.scroll(TimeValue.timeValueMinutes(30));
        SearchResponse searchScrollResponse = null;
        try {
            searchScrollResponse = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
            return searchScrollResponse;
        } catch (IOException e) {
            e.printStackTrace();
            throw new EsExecption("搜索异常");
        }
    }


    /**
     * 清除scrollId
     *
     * @param scrollId
     * @return
     */
    public Boolean clearScroll(String scrollId) {
        ClearScrollRequest request = new ClearScrollRequest();
        request.addScrollId(scrollId);
        try {
            ClearScrollResponse response = restHighLevelClient.clearScroll(request, RequestOptions.DEFAULT);
            boolean success = response.isSucceeded();
            return success;
        } catch (IOException e) {
            e.printStackTrace();
            logger.info("释放scroll出错，scrollId:{}", scrollId);
            throw new EsExecption("释放scroll 出错");
        }

    }


    public SearchResponse searchAfter(String index, SearchSourceBuilder sourceBuilder, Object[] searchAfter) {
        if (searchAfter != null && searchAfter.length > 0) {
            sourceBuilder.searchAfter(searchAfter);
        }
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.source(sourceBuilder);
        logger.info("es 查询语句：{}", sourceBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            return searchResponse;
        } catch (IOException e) {
            e.printStackTrace();
            throw new EsExecption("搜索异常");
        }
    }
}
